package com.mybatis.jpa.utils;

import com.mybatis.jpa.exception.JpaException;
import com.mybatis.jpa.repository.CrudRepository;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.mapping.MappedStatement;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

public class MappedStatementUtils {

    private static final ConcurrentHashMap<String, Class> map = new ConcurrentHashMap<>();

    /**
     * 获取调用方法名
     * @param mappedStatement
     * @return
     */
    public static String getClassMethod(MappedStatement mappedStatement) {
        String id = mappedStatement.getId();
        return id.substring(id.lastIndexOf('.') + 1);
    }

    /**
     * 获取调用接口的路径
     * @param mappedStatement
     * @return
     */
    public static String getClassPath(MappedStatement mappedStatement) {
        String id = mappedStatement.getId();
        return id.substring(0, id.lastIndexOf('.'));
    }

    public static Class getEntityClass(MappedStatement mappedStatement) {
        String classPath = getClassPath(mappedStatement);
        Class clazz = getMapperRegister(classPath, mappedStatement);
        Type[] types = clazz.getGenericInterfaces();

        JpaException.ArrayIsBlankRuntimeException(types, "没有继承CrudRepository接口，class=%s",clazz);
        Type targetType = Arrays.asList(types)
                .stream()
                .filter(type -> StringUtils.startsWith(type.getTypeName(), CrudRepository.class.getName()))
                .findFirst()
                .orElseThrow(() -> new IllegalStateException("没有继承CrudRepository接口"));

        return (Class) ((ParameterizedType)targetType).getActualTypeArguments()[0];
    }

    private static Class<?> getMapperRegister(String classPath, MappedStatement mappedStatement) {
        if (map.containsKey(classPath)) {
            return map.get(classPath);
        }

        MapperRegistry mapperRegistry = mappedStatement.getConfiguration().getMapperRegistry();
        Optional<Class<?>> optional = mapperRegistry.getMappers().stream()
                .filter(clazz -> StringUtils.equalsIgnoreCase(clazz.getName(), classPath)).findFirst();

        map.put(classPath, optional.get());
        return map.get(classPath);
    }
}
